package cn.leaf.calculator.convert;

import cn.hutool.core.util.ReflectUtil;
import cn.leaf.calculator.CalException;
import cn.leaf.calculator.annotation.FormulaBeanCalItem;
import cn.leaf.calculator.annotation.FormulaBeanId;
import cn.leaf.calculator.annotation.FormulaBeanName;
import cn.leaf.calculator.annotation.FormulaBeanString;
import cn.leaf.calculator.bean.CalUnitBeanDefinition;
import cn.leaf.calculator.bean.StandardCalUnitBean;
import cn.leaf.calculator.resolver.FormulaBeanDefinitionResolver;


import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;

/**
 * 注解的公式转换器
 *
 * @author 李克国
 * @version 1.0.0
 */
public class AnnotationFormulaDefinitionConvert<T> implements AnnotationFormulaConvert<T> {

    private final Class<T> beanClassDefinition;

    private Field idField;

    private Field stringField;

    private Field nameField;

    private Collection<Field> calItemFields;

    private FormulaBeanDefinitionResolver<T> resolver;

    public AnnotationFormulaDefinitionConvert(Class<T> beanClassDefinition) {
        this.beanClassDefinition = beanClassDefinition;
        initialize();
    }

    @Override
    public CalUnitBeanDefinition<T> convert(T source) {
        CalUnitBeanDefinition<T> result = new StandardCalUnitBean<>();
        result.setValue(source);
        // 装配id
        Object idValue = ReflectUtil.getFieldValue(source, idField);
        assertForTrue(idValue == null, "id为必须字段");
        assertForTrue(!(idValue instanceof Serializable), "id的类型必须为 java.io.Serializable 的实现");
        result.setId(((Serializable) idValue));
        // 装配name
        if (nameField != null) {
            Object nameValue = ReflectUtil.getFieldValue(source, nameField);
            if (nameValue != null) {
                result.setName(nameValue.toString());
            }
        }
        // 装配formulaString
        if (stringField != null) {
            Object formulaStringValue = ReflectUtil.getFieldValue(source, stringField);
            if (formulaStringValue != null) {
                result.setFormulaString(formulaStringValue.toString());
            }
        }
        result.setResolver(resolver);
        return result;
    }

    @Override
    public Class<T> getDefinition() {
        return beanClassDefinition;
    }

    public void initialize() {
        Field[] fields = beanClassDefinition.getDeclaredFields();
        calItemFields = new ArrayList<>();
        for (Field field : fields) {
            if (hasTargetAnnotation(field, FormulaBeanId.class)) {
                assertForTrue(idField != null, "id 必须是唯一的字段");
                idField = field;
            }
            if (hasTargetAnnotation(field, FormulaBeanName.class)) {
                nameField = field;
            }
            if (hasTargetAnnotation(field, FormulaBeanString.class)) {
                stringField = field;
            }
            if (hasTargetAnnotation(field, FormulaBeanCalItem.class)) {
                calItemFields.add(field);
            }
        }
        assertForTrue(idField == null, "初始化失败：未找到FormulaBeanId 标注的字段，无法分析BeanDefinition");
    }

    private T createNewTarget() {
        T t = null;
        try {
            t = beanClassDefinition.newInstance();
        } catch (Exception e) {
            assertForTrue(true, "%s:详细信息：%s", "创建目标对象错误，目标对象的ClassDefinition可能不存在空参构造器", e.getMessage());
            e.printStackTrace();
        }
        return t;
    }

    @Override
    public Collection<Field> getCalItemFields() {
        return calItemFields;
    }

    @Override
    public FormulaBeanDefinitionResolver<T> getResolver() {
        return resolver;
    }

    @Override
    public void setResolver(FormulaBeanDefinitionResolver<T> resolver) {
        this.resolver = resolver;
    }

    private <S extends Annotation> boolean hasTargetAnnotation(Field field, Class<S> annotationClass) {
        return field.getAnnotation(annotationClass) != null;
    }

    private void assertForTrue(boolean valid, String message) {
        assertForTrue(valid, message, (Object[]) null);
    }

    private void assertForTrue(boolean valid, String message, Object... args) {
        if (valid) {
            throw new CalException("FormulaBeanConvertException:" + String.format(message, args));
        }
    }
}
